#!/usr/bin/env python
# xml tree <--> sqltree converter

# $Id$
"""
Options creating and postprocessing

"""
from version import __version__

import codecs
from optparse import OptionParser, OptionGroup , OptionValueError
from main_pack import pack_utils

def _dsn ( options ) : # make option.dsn string from connection options
    tmp = 'dbname=%s user=%s' % (options.dbname,options.user)
    if options.host : tmp += ' host=%s port=%d' % (options.host,options.port)
    if options.password : tmp += ' password='+options.password
    options.dsn = tmp


def process_options ( parser , options , args , user , password=None ) :
    # dbname may be : in -D option, first in args or default - which first.
    if options.dbname is None :
        if args : options.dbname = args.pop(0)
        else : options.dbname = user
        
    # user may be : in -U option, second in args or default - which first.
    if options.user is None :
        if args : options.user = args.pop(0)
        else : options.user = user
        
    if options.askpassword : options.password = password or getpass.getpass()

    _dsn ( options )
     
    if options.tables is not None :     # may be empty string to prevent all
        options.tables = filter(None,options.tables.split(','))
        
    if options.debug :
        for k in options.__dict__ :
            print >>sys.stderr , k , ':\t' , getattr(options,k)
        print >>sys.stderr , 'args:' , args

def _callback_debug ( option , opt , value , parser ) :
    if value<0 or value>4 :
        raise OptionValueError ( 'debug level must be between 0 and 4' )
    setattr ( parser.values , option.dest , value )

def _callback_translations ( option , opt , value , parser ) :

    if value.startswith('@') :
        f = file ( value[1:] )
        L = [ s.strip() for s in f.read().splitlines() ]
    else : L = value.split(',')

    L = [ tuple(filter(None,s.split('='))) for s in L ]
    badlist = filter(lambda x : len(x)!=2,L)
    if badlist : raise OptionValueError (
        'invalid list element: %s' % ('='.join(badlist[0])) )

    setattr ( parser.values , option.dest , L )

def _callback_encoding ( option , opt , value , parser ) :
    try: codecs.lookup ( value )
    except LookupError : raise OptionValueError ( '"%s" invalid encoding' % value )
    setattr ( parser.values , option.dest , value )

def _callback_extract ( option , opt , value , parser ) :
    L = value.split(',')
    res = []
    for s in L :
        try:
            if '-' in s : left,right = s.split('-')
            else : left = right = s
            
            if left : left = int(left)
            else : left=0
            if right : right = int(right)
            else : right = 9223372036854775807 # max bigint
            assert left<=right
        except : raise OptionValueError ( 'bad interval : '+s )
        res.append ((left,right))
    setattr ( parser.values , option.dest , res )

def create_options ( user ) :

    usage = '%prog [OPTIONS]... [[DBNAME] USER] [FILE]...'
    version = '%prog ' + __version__
    description = (
        'Store/restore xml file to/from PostgreSQL database. ' 
        'If no files given - process standard input.'
        )

    parser = OptionParser( usage , description=description , version=version )
    
    parser.add_option ( '-d' , dest='debug' , type='int' , default=0 ,
                        metavar='LEVEL' , help='debug level "0-4"' ,
                        action='callback' , callback = _callback_debug )

    parser.add_option ( '-q' , '--quiet' , action='store_true' ,
                        help='do not print extra information' )

    group = OptionGroup(parser,"Connection options")
    #                           ------------------
    group.add_option ( '-D' , '--dbname' ,
        help='connect to DBNAME (default: first argument or "%s")'%user )
    group.add_option ( '--host' , help='connect to HOST (default "local")' )
    group.add_option ( '-p' , '--port' , type='int' , default=5432 ,
                       help='connect to PORT (default "5432")' )
    group.add_option ( '-U' , '--user' ,
        help='connect as USER (default second argument or "%s")'%user )
    group.add_option ( '-P' , '--password' ,
        help='connect with (DANGER - exposed in top!!!)' )
    group.add_option ( '-W' , '--askpassword' , action='store_true' ,
        help='ask password from terminal (safe).' )
    parser.add_option_group ( group )

    group = OptionGroup(parser,"XML Storage options")
    #                           -------------------
    group.add_option ( '-s' , '--schema' , default='public' ,
        help='schema for xml stuff in database (default "public")' )
    group.add_option ( '-t' , '--tables' , metavar='LISTOFTABLES' ,
        help='comma separated list of tables in SCHEMA, which store xml '
        'attributes. Default - all tables in SCHEMA, which have '
        '"attr_list" column except "fragment_forest" and "attr_list" itself.'
        ' Write just comma to disable all tables.'
        )
    parser.add_option_group ( group )

    group = OptionGroup(parser,"XML Translation options")
    #                           -----------------------
    group.add_option ( '-g' , '--tags' , metavar='LIST' ,
        type='string' , dest='tags' ,
        action = 'callback' , callback=_callback_translations ,
        help='comma separated list of "XMLTAGNAME=SQLTAGNAME"'
        ' or "@FILENAME" where file FILENAME contains newline separated list.' )
    group.add_option ( '-a' , '--attributes' , metavar='LIST' ,
        type='string' , dest='attributes' ,
        action = 'callback' , callback=_callback_translations ,
        help='comma separated list of "XMLATTRNAME=SQLATTRNAME"'
        ' or "@FILENAME" where file FILENAME contains newline separated list.' )

    parser.add_option_group ( group )

    group = OptionGroup(parser,"Extraction options")
    #                           ------------------
    group.add_option ( '-e' , '--encoding' , default='utf-8' ,
                       dest='encoding' , action='callback' ,
                       type='string' , callback=_callback_encoding ,
                       help='output encoding (default "utf-8")' )
    group.add_option (
        '-x' , '--extract' , metavar='LIST' ,
        type='string' , dest='extract' , action='callback' ,
        callback = _callback_extract ,
        help='comma separated list of integer document numbers or intervals '
        '(- delimited). "-" - all, "25-" - from 25 to maximum, '
        '"-25" - from 0 to 25.' )
    group.add_option (
        '-c' , '--comment' , action='store_true' ,
        help='comment each document with document number in first string' )
    group.add_option (
        '-o' , '--output' , metavar='FILE' ,
        help='write extracted documents to FILE ("-" to stdout). '
        ' If FILE contains "%" - it represents format string '
        'for one argument - '
        ' document number, so each document will be written to separate file. '
        ' For example "doc%05d.xml"' )

    parser.add_option_group ( group )

    group = OptionGroup(parser,"Administration options")
    #                           ----------------------
    group.add_option (
        '--info' , action='store_true' , help='show information' )
    group.add_option (
        '-l' , '--list' , action='store_true' ,
        help='show first tag of documents (see --extract)' )
    group.add_option ( '--fill' ,  metavar='TABLE' ,
                       help='fill TABLE with xml data (just after creation)' )
    group.add_option (
        '--free' ,  metavar='TABLE' ,
        help='free TABLE from xml data (before deletion)' )
    group.add_option (
        '--init' ,  action='store_true' ,
        help='init xml storage data (DANGER! deletes ALL old xml data!)' )
    group.add_option (
        '--pack' ,  type='choice' ,
        choices = [ k for k,v in pack_utils]+['all'] , metavar='OBJECTS' ,
        help='pack xml data. OBJECTS - one of '
        +', '.join(['"'+t+'"' for t,v in pack_utils])
        + ' or "all".' )

    parser.add_option_group ( group )
    return parser
#===========================================================
